<template>
  <div class="node-monitor-container">
    <PageWrapper :title="t('sys.menu.menuList.queryNoteList')" :useHeaderWrapper="true">
      <!-- 顶部概览卡片 -->
      <Card class="overview-card mb-4">
        <div class="overview-content">
          <!-- 设备分组 -->
          <div class="overview-group">
            <div class="group-title">设备信息</div>
            <div class="overview-content-flexs">
              <div class="overview-item">
                <div class="overview-label">{{ t('sys.queryNoteList.deviceNodes') }}</div>
                <div class="overview-value normal-count">{{ devicenum }}</div>
              </div>
              <div class="overview-divider"></div>
              <div class="overview-item">
                <div class="overview-label">{{ t('sys.devstatistics.deviceOffLineCount') }}</div>
                <div class="overview-value abnormal-count">{{ devices.length - devicenum }}</div>
              </div>
            </div>
          </div>

          <div class="overview-group-divider"></div>

          <!-- 节点分组 -->
          <div class="overview-group">
            <div class="group-title">节点状态</div>
            <div class="overview-content-flexs">
              <div class="overview-item">
                <div class="overview-label">{{ t('sys.queryNoteList.abnormalNodes') }}</div>
                <div class="overview-value abnormal-count">{{
                  activeKey && (tabsactiveKey == '1' || tabsactiveKey == '3')
                    ? filteredAbnormalNoteList.length - timeLength
                    : filteredAbnormalNoteList.length
                }}</div>
              </div>
              <div class="overview-divider"></div>
              <div class="overview-item">
                <div class="overview-label">{{ t('sys.queryNoteList.timeNode') }}</div>
                <div class="overview-value abnormal-count">{{ timeLength }}</div>
              </div>
            </div>
          </div>

          <div class="overview-group-divider"></div>

          <!-- 运行状态分组 -->
          <div class="overview-group">
            <div class="group-title">运行状态</div>
            <div class="overview-content-flexs">
              <div class="overview-item">
                <div class="overview-label">{{ '繁忙' }}</div>
                <div class="overview-value normal-count">{{ Number(getDeviceInfoNUm.busy) }}</div>
              </div>
              <div class="overview-divider"></div>
              <div class="overview-item">
                <div class="overview-label">{{ '空闲' }}</div>
                <div class="overview-value normal-count">{{ Number(getDeviceInfoNUm.free) }}</div>
              </div>
              <div class="overview-divider"></div>
              <div class="overview-item">
                <div class="overview-label">{{ '离线' }}</div>
                <div class="overview-value abnormal-count">{{
                  Number(getDeviceInfoNUm.offline)
                }}</div>
              </div>
            </div>
          </div>
          <div>
            <div class="overview-item full-width">
              <div class="overview-label">{{ t('sys.queryNoteList.lastRefreshTime') }}</div>
              <div class="overview-time">{{ lastRefreshTime }}</div>
            </div>
          </div>
          <div>
            <div class="overview-item full-width">
              <div class="overview-label">{{ t('sys.queryNoteList.htmlTemplate') }}</div>
              <div class="overview-time">
                <a-switch
                  v-model:checked="TemPlechecked1"
                  checkedValue="1"
                  unCheckedValue="0"
                  checked-children="开"
                  un-checked-children="关"
                />
              </div>
            </div>
          </div>
          <div>
            <div class="overview-item full-width">
              <div class="overview-label">{{ t('sys.queryNoteList.clearLog') }}</div>
              <div>
                <a-popconfirm
                  title="确认清空所有异常节点吗？"
                  ok-text="确认"
                  ok-type="danger"
                  @confirm="clearAllAbnormalNodeListClick"
                >
                  <a-button shape="round" type="primary">
                    {{ t('sys.queryNoteList.clearLog') }}
                  </a-button>
                </a-popconfirm>
              </div>
            </div>
          </div>
        </div>
      </Card>

      <!-- 接口运行异常卡片 -->
      <Card v-if="systemRunningConditionList.length > 0" class="mb-4" title="接口运行异常列表">
        <a-table
          :columns="systemRunningConditionColumns"
          :dataSource="systemRunningConditionList"
          :pagination="false"
          rowKey="url"
          size="small"
        >
          <template #bodyCell="{ column, record }">
            <template v-if="column.key === 'url'">
              <span style="color: #1890ff; font-family: monospace">{{ record.url }}</span>
            </template>
            <template v-else-if="column.key === 'type'">
              <a-tag color="red">{{ record.type }}</a-tag>
            </template>
            <template v-else-if="column.key === 'result'">
              <a-tooltip :title="record.result">
                <span
                  style="
                    display: inline-block;
                    max-width: 300px;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                  "
                >
                  {{ record.result }}
                </span>
              </a-tooltip>
            </template>
            <template v-else-if="column.key === 'failCount'">
              <a-tag color="red">{{ record.failCount }}</a-tag>
            </template>
          </template>
        </a-table>
      </Card>

      <!-- 内容区域 -->
      <div class="content-wrapper">
        <!-- 正常节点区域 -->
        <a-row>
          <!-- 左侧设备和节点树形结构 -->
          <a-col :span="4">
            <Card title="设备和节点" class="tree-card">
              <a-tree
                v-model:selectedKeys="selectedKeys"
                @expand="handleExpand"
                :treeData="filtertreeData"
                :bodyStyle="{ padding: '20px 10px' }"
                :loading="loadingTreeData"
                :expanded-keys="expandedKeys"
                :accordion="true"
                expandAction="click"
                @select="onSelectChange"
              >
                <template #title="{ title, key, ip, deviceStatus, status, nodeStatus }">
                  <span>
                    <span
                      v-if="status == '设备'"
                      :class="['status-dot', deviceStatus == 0 ? 'red-dot' : 'green-dot']"
                    ></span>
                    <span v-if="ip"> {{ title }} ({{ ip }}) </span>
                    <span v-else> {{ title }} </span>

                    <span v-if="status == '节点'">
                      <a-tag v-if="nodeStatus == 0" color="red">{{
                        t('sys.analysis.offLine')
                      }}</a-tag>
                      <a-tag v-else-if="nodeStatus == 2" color="green">{{
                        t('sys.configuration.busy')
                      }}</a-tag>
                      <a-tag v-else-if="nodeStatus == 1" color="orange">{{
                        t('sys.configuration.leisure')
                      }}</a-tag>
                      <a-tag v-else-if="nodeStatus == 999" color="red">{{
                        t('sys.configuration.abnormal')
                      }}</a-tag>
                    </span>
                  </span>
                </template>
              </a-tree>
            </Card>
          </a-col>
          <!-- 右侧节点显示区域 -->
          <a-col :span="20">
            <a-collapse accordion @change="handleChange" v-model:activeKey="activeKey">
              <a-collapse-panel v-for="(item, index) in devices" :key="item.deviceMac">
                <template #header>
                  <span>{{ item.deviceMac }}({{ item.deviceIp }})</span>
                </template>
                <div class="node-section">
                  <div class="section-header">
                    <h3 class="section-title">
                      <Icon
                        icon="ant-design:check-circle-outlined"
                        class="title-icon normal-icon"
                      />
                      {{ t('sys.queryNoteList.normalNodes') }}
                    </h3>
                  </div>
                  <a-table
                    v-if="TemPlechecked1 == '1'"
                    :columns="normalColumns"
                    :dataSource="filteredDeviceNoteList"
                    :pagination="false"
                    :rowKey="(record) => record.taskId || `${record.macAddress}_${record.nodeName}`"
                  >
                    <template #bodyCell="{ column, record, index1 }">
                      <template v-if="column.dataIndex == 'status'">
                        <a-tag v-if="record.status === -1" color="green">繁忙</a-tag>
                        <a-tag
                          v-else-if="record.status !== undefined && record.status !== null"
                          color="orange"
                          >空闲</a-tag
                        >
                        <!-- <a-tag v-else color="red">离线</a-tag> -->
                      </template>
                      <template v-else-if="column.dataIndex == 'action'">
                        <a-button type="link" @click="openRealtimeLog(record)">
                          查看实时日志
                        </a-button>
                        <a-button @click="detailsClick(record)" type="link">
                          {{ t('sys.device.columns.log') }}
                        </a-button>
                        <!-- <a-dropdown trigger="click">
                          <a-button type="link" @click.prevent>
                            {{ t('sys.configuration.action') }}
                          </a-button>
                          <template #overlay>
                            <a-menu>
                              <a-menu-item key="1">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'start')"
                                  :title="t('sys.configuration.Isstart')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{
                                    t('sys.configuration.start')
                                  }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item key="2">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'restart')"
                                  :title="t('sys.configuration.Isstart')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{ t('sys.analysis.restart') }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item key="3">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'kill')"
                                  :title="t('sys.configuration.Iscease')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{
                                    t('sys.configuration.cease')
                                  }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item v-for="(item, index) in nodeCmds" :key="item.cmdContent">
                                <a-popconfirm
                                  class="custom-popconfirm"
                                  @confirm="startconfirm(record, item.cmdContent)"
                                  :title="t('sys.configuration.IS') + item.cmdName"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{ item.cmdName }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                            </a-menu>
                          </template>
                        </a-dropdown> -->
                      </template>
                    </template>
                  </a-table>

                  <div
                    v-if="filteredDeviceNoteList.length > 0 && TemPlechecked1 == '0'"
                    class="card-grid enhanced-grid"
                  >
                    <div
                      v-for="(node, index) in filteredDeviceNoteList"
                      :key="index"
                      class="node-card normal-card enhanced-card"
                    >
                      <div class="card-header">
                        <div class="card-title">{{
                          node.nodeName || t('sys.queryNoteList.unknownNode')
                        }}</div>
                        <div class="flex">
                          <a-button
                            class="mr-3"
                            type="primary"
                            size="small"
                            @click="detailsClick(node)"
                          >
                            {{ t('sys.device.columns.log') }}
                          </a-button>
                          <a-dropdown trigger="click">
                            <a-button class="mr-3" type="primary" size="small" @click.prevent>
                              {{ t('sys.configuration.action') }}
                            </a-button>
                            <template #overlay>
                              <a-menu>
                                <a-menu-item key="1">
                                  <a-popconfirm
                                    @confirm="startconfirm(node, 'start')"
                                    :title="t('sys.configuration.Isstart')"
                                    :ok-text="t('sys.configuration.comfig')"
                                    :cancel-text="t('sys.configuration.cancel')"
                                  >
                                    <a-button type="link">{{
                                      t('sys.configuration.start')
                                    }}</a-button>
                                  </a-popconfirm>
                                </a-menu-item>
                                <a-menu-item key="2">
                                  <a-popconfirm
                                    @confirm="startconfirm(node, 'restart')"
                                    :title="t('sys.configuration.Isstart')"
                                    :ok-text="t('sys.configuration.comfig')"
                                    :cancel-text="t('sys.configuration.cancel')"
                                  >
                                    <a-button type="link">{{ t('sys.analysis.restart') }}</a-button>
                                  </a-popconfirm>
                                </a-menu-item>
                                <a-menu-item key="3">
                                  <a-popconfirm
                                    @confirm="startconfirm(node, 'kill')"
                                    :title="t('sys.configuration.Iscease')"
                                    :ok-text="t('sys.configuration.comfig')"
                                    :cancel-text="t('sys.configuration.cancel')"
                                  >
                                    <a-button type="link">{{
                                      t('sys.configuration.cease')
                                    }}</a-button>
                                  </a-popconfirm>
                                </a-menu-item>
                                <a-menu-item
                                  v-for="(item, index) in nodeCmds"
                                  :key="item.cmdContent"
                                >
                                  <a-popconfirm
                                    class="custom-popconfirm"
                                    @confirm="startconfirm(node, item.cmdContent)"
                                    :title="t('sys.configuration.IS ') + item.cmdName"
                                    :ok-text="t('sys.configuration.comfig')"
                                    :cancel-text="t('sys.configuration.cancel')"
                                  >
                                    <a-button type="link">{{ item.cmdName }}</a-button>
                                  </a-popconfirm>
                                </a-menu-item>
                              </a-menu>
                            </template>
                          </a-dropdown>
                          <Tag
                            :color="node.outTime && Number(node.outTime) < 300 ? 'red' : 'green'"
                            class="outtime-tag"
                          >
                            {{ node.outTime }} 秒
                          </Tag>
                        </div>
                      </div>

                      <div class="card-body">
                        <div class="info-item">
                          <span class="info-label">MAC地址:</span>
                          <span class="info-value">{{ node.macAddress || '-' }}</span>
                        </div>
                        <div class="info-item">
                          <span class="info-label">任务ID:</span>
                          <span class="info-value">{{ node.taskId || '-' }}</span>
                        </div>
                        <div class="info-item">
                          <span class="info-label">创建时间:</span>
                          <span class="info-value">{{ node.createTime || '-' }}</span>
                        </div>
                        <div v-if="node.remark" class="info-item full-width">
                          <span class="info-label">备注:</span>
                          <span class="info-value ellipsis">{{ node.remark }}</span>
                        </div>
                      </div>
                    </div>
                  </div>

                  <div
                    v-if="filteredDeviceNoteList.length < 0 && TemPlechecked1 == '0'"
                    class="empty-state"
                  >
                    <Empty
                      :description="
                        selectedKeys.length > 0
                          ? '没有符合条件的正常节点'
                          : t('sys.queryNoteList.noNormalNodes')
                      "
                      :imageSize="80"
                    />
                  </div>
                </div>
                <!-- 异常节点区域 -->
                <a-tabs v-model:activeKey="tabsactiveKey">
                  <a-tab-pane key="1" tab="全部" />
                  <a-tab-pane key="2" tab="异常" />
                  <a-tab-pane key="3" tab="超时" />
                </a-tabs>
                <div class="node-section mt-6">
                  <div class="section-header">
                    <h3 class="section-title">
                      <Icon
                        icon="ant-design:close-circle-outlined"
                        class="title-icon abnormal-icon"
                      />
                      {{ t('sys.queryNoteList.abnormalNodes') }}
                    </h3>
                  </div>
                  <a-table
                    v-if="TemPlechecked1 == '1'"
                    :columns="normalColumns1"
                    :dataSource="filteredAbnormalNoteList"
                    :pagination="false"
                    rowKey="nodeName"
                  >
                    <template #bodyCell="{ column, record }">
                      <template v-if="column.dataIndex == 'action'">
                        <a-button @click="detailsClick(record)" type="link">
                          {{ t('sys.device.columns.log') }}
                        </a-button>
                        <a-popconfirm
                          @confirm="recoveryNodeClick(record)"
                          :title="t('sys.configuration.IsrecoveryNode')"
                          :ok-text="t('sys.configuration.comfig')"
                          :cancel-text="t('sys.configuration.cancel')"
                        >
                          <a-button class="mr-3" type="link">
                            {{ t('sys.device.columns.recoveryNode') }}
                          </a-button>
                        </a-popconfirm>
                        <a-dropdown trigger="click">
                          <a-button type="link" @click.prevent>
                            {{ t('sys.configuration.action') }}
                          </a-button>
                          <template #overlay>
                            <a-menu>
                              <a-menu-item key="1">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'start')"
                                  :title="t('sys.configuration.Isstart')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{
                                    t('sys.configuration.start')
                                  }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item key="2">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'restart')"
                                  :title="t('sys.configuration.Isstart')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{ t('sys.analysis.restart') }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item key="3">
                                <a-popconfirm
                                  @confirm="startconfirm(record, 'kill')"
                                  :title="t('sys.configuration.Iscease')"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{
                                    t('sys.configuration.cease')
                                  }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                              <a-menu-item v-for="(item, index) in nodeCmds" :key="item.cmdContent">
                                <a-popconfirm
                                  class="custom-popconfirm"
                                  @confirm="startconfirm(record, item.cmdContent)"
                                  :title="t('sys.configuration.IS') + item.cmdName"
                                  :ok-text="t('sys.configuration.comfig')"
                                  :cancel-text="t('sys.configuration.cancel')"
                                >
                                  <a-button type="link">{{ item.cmdName }}</a-button>
                                </a-popconfirm>
                              </a-menu-item>
                            </a-menu>
                          </template>
                        </a-dropdown>
                      </template>
                    </template>
                  </a-table>
                  <div
                    v-if="filteredAbnormalNoteList.length > 0 && TemPlechecked1 == '0'"
                    class="card-grid"
                  >
                    <div
                      v-for="(node, index) in filteredAbnormalNoteList"
                      :key="index"
                      class="node-card abnormal-card"
                    >
                      <div>
                        <div class="card-header">
                          <div class="card-title">{{
                            node.nodeName || t('sys.queryNoteList.unknownNode')
                          }}</div>

                          <div class="flex">
                            <a-button
                              class="mr-3"
                              type="primary"
                              size="small"
                              @click="detailsClick(node)"
                            >
                              {{ t('sys.device.columns.log') }}
                            </a-button>
                            <a-popconfirm
                              @confirm="recoveryNodeClick(node)"
                              :title="t('sys.configuration.IsrecoveryNode')"
                              :ok-text="t('sys.configuration.comfig')"
                              :cancel-text="t('sys.configuration.cancel')"
                            >
                              <a-button class="mr-3" type="primary" size="small">
                                {{ t('sys.device.columns.recoveryNode') }}
                              </a-button>
                            </a-popconfirm>

                            <a-dropdown trigger="click">
                              <a-button class="mr-3" type="primary" size="small" @click.prevent>
                                {{ t('sys.configuration.action') }}
                              </a-button>
                              <template #overlay>
                                <a-menu>
                                  <a-menu-item key="1">
                                    <a-popconfirm
                                      @confirm="startconfirm(node, 'start')"
                                      :title="t('sys.configuration.Isstart')"
                                      :ok-text="t('sys.configuration.comfig')"
                                      :cancel-text="t('sys.configuration.cancel')"
                                    >
                                      <a-button type="link">{{
                                        t('sys.configuration.start')
                                      }}</a-button>
                                    </a-popconfirm>
                                  </a-menu-item>
                                  <a-menu-item key="2">
                                    <a-popconfirm
                                      @confirm="startconfirm(node, 'restart')"
                                      :title="t('sys.configuration.Isstart')"
                                      :ok-text="t('sys.configuration.comfig')"
                                      :cancel-text="t('sys.configuration.cancel')"
                                    >
                                      <a-button type="link">{{
                                        t('sys.analysis.restart')
                                      }}</a-button>
                                    </a-popconfirm>
                                  </a-menu-item>
                                  <a-menu-item key="3">
                                    <a-popconfirm
                                      @confirm="startconfirm(node, 'kill')"
                                      :title="t('sys.configuration.Iscease')"
                                      :ok-text="t('sys.configuration.comfig')"
                                      :cancel-text="t('sys.configuration.cancel')"
                                    >
                                      <a-button type="link">{{
                                        t('sys.configuration.cease')
                                      }}</a-button>
                                    </a-popconfirm>
                                  </a-menu-item>
                                  <a-menu-item
                                    v-for="(item, index) in nodeCmds"
                                    :key="item.cmdContent"
                                  >
                                    <a-popconfirm
                                      class="custom-popconfirm"
                                      @confirm="startconfirm(node, item.cmdContent)"
                                      :title="t('sys.configuration.IS ') + item.cmdName"
                                      :ok-text="t('sys.configuration.comfig')"
                                      :cancel-text="t('sys.configuration.cancel')"
                                    >
                                      <a-button type="link">{{ item.cmdName }}</a-button>
                                    </a-popconfirm>
                                  </a-menu-item>
                                </a-menu>
                              </template>
                            </a-dropdown>
                            <Badge
                              :count="node.deviceTimeOutNumber || 0"
                              :overflow-count="99"
                              class="timeout-badge"
                            >
                              <Tag color="red">超时</Tag>
                            </Badge>
                          </div>
                        </div>

                        <div class="card-body">
                          <div class="info-item">
                            <span class="info-label">MAC地址:</span>
                            <span class="info-value">{{ node.macAddress || '-' }}</span>
                          </div>
                          <div class="info-item">
                            <span class="info-label">超时次数:</span>
                            <span class="info-value timeout-count">{{
                              node.deviceTimeOutNumber || 0
                            }}</span>
                          </div>
                          <div class="info-item">
                            <span class="info-label"
                              >{{ t('sys.informaionMatchaing.columns.lastTimeOutTime') }}:</span
                            >
                            <span class="info-value timeout-count">{{
                              node.lastTimeOutTime || 0
                            }}</span>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>

                  <div
                    v-if="filteredAbnormalNoteList.length < 0 && TemPlechecked1 == '0'"
                    class="empty-state"
                  >
                    <Empty
                      :description="
                        selectedKeys.length > 0
                          ? '没有符合条件的异常信息'
                          : t('sys.queryNoteList.noAbnormalNodes')
                      "
                      :imageSize="80"
                    />
                  </div>
                </div>
              </a-collapse-panel>
            </a-collapse>
          </a-col>
        </a-row>
      </div>
      <a-modal
        v-model:visible="ModalShow"
        :title="
          detailsRecord?.macAddress +
          '-' +
          detailsRecord?.nodeName +
          '-' +
          t('sys.device.columns.log')
        "
        :width="900"
        :footer="null"
      >
        <a-table
          :loading="deviceLogFilePathLoading"
          :pagination="deviceLogFilePathPagination"
          :columns="deviceLogFilePathColumns"
          :dataSource="deviceLogFilePathList"
          @change="handleDeviceLogFilePathChange"
          bordered
          rowKey="url"
        >
          <template #bodyCell="{ column, record }">
            <template v-if="column.dataIndex === 'url'">
              <a-button @click="handleFileDownload(record)" type="link">
                {{ record.url }}
              </a-button>
            </template>
            <template v-else-if="column.dataIndex === 'size'">
              {{ formatFileSize(record.size) }}
            </template>
            <template v-else-if="column.dataIndex === 'modified'">
              {{ formatDateTime(record.modified) }}
            </template>
          </template>
        </a-table>
      </a-modal>

      <!-- 实时日志模态框 -->
      <a-modal
        v-model:visible="realtimeLogModalVisible"
        :title="`实时日志 - ${realtimeLogRecord?.macAddress || ''} - ${realtimeLogRecord?.nodeName || ''}`"
        :width="1200"
        :footer="null"
        @cancel="closeRealtimeLog"
      >
        <div class="realtime-log-container">
          <div class="log-header">
            <div class="log-status-info">
              <span
                v-if="logStats && logIsConnected && !logComplete"
                class="log-status-badge tailing"
              >
                🔄 实时跟踪中...
              </span>
              <span v-if="logStats" class="log-stats-header">
                已接收: {{ logStats.totalLines }} 行
              </span>
              <span v-if="logComplete" class="log-complete-badge"> ✅ 读取完成 </span>
              <span v-if="!logIsConnected" class="log-status-badge disconnected"> ⚠️ 未连接 </span>
            </div>
            <div class="log-actions">
              <a-button size="small" @click="clearLogLines">清空日志</a-button>
              <a-button size="small" @click="toggleLogConnection">
                {{ logIsConnected ? '断开连接' : '重新连接' }}
              </a-button>
            </div>
          </div>
          <div class="log-display-container" ref="logContainerRef">
            <div v-for="(logLine, index) in logLines" :key="index" class="log-line-item">
              <span class="log-line-number" v-if="logLine.line_number">{{
                logLine.line_number
              }}</span>
              <span class="log-line-content">{{ logLine.content }}</span>
              <span class="log-line-timestamp" v-if="logLine.timestamp">{{
                formatLogTimestamp(logLine.timestamp)
              }}</span>
            </div>
            <div v-if="logLines.length === 0 && logIsConnected && !logComplete" class="log-loading">
              正在加载日志内容...
            </div>
            <div v-if="logLines.length === 0 && !logIsConnected" class="log-loading">
              等待连接...
            </div>
          </div>
        </div>
      </a-modal>
    </PageWrapper>
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted, onUnmounted, computed, nextTick } from 'vue';
  import { Card, Tag, Empty, Badge, Tree, message } from 'ant-design-vue';
  import { PageWrapper } from '@/components/Page';
  import {
    queryNoteList,
    senddeviceNodecmd,
    getDeviceInfo,
    postCmdPage,
    handleTimeoutMacNodeStatus,
    clearAllAbnormalNodeList,
  } from '@/api/demo/device';
  import { useI18n } from '@/hooks/web/useI18n';
  import { getDeviceListAPI, getNodeListAPI, getSystemRunningCondition } from '@/api/demo/analysis';
  // 响应式数据
  const { t } = useI18n();

  // SSE 连接相关（节点数据）
  const isSSEConnected = ref(false);
  let eventSource: EventSource | null = null;

  // 实时日志 SSE 连接相关
  const realtimeLogModalVisible = ref(false);
  const realtimeLogRecord = ref<any>(null);
  const logIsConnected = ref(false);
  let logEventSource: EventSource | null = null;
  const logLines = ref<any[]>([]);
  const logStats = ref<{ totalLines: number } | null>(null);
  const logComplete = ref(false);
  const logContainerRef = ref<HTMLElement | null>(null);

  // SSE 服务器配置（独立的 IP 和端口，仅在此页面使用）
  // 注意：此 SSE 服务器的 IP 和端口与主 API 服务器不同
  // IP 地址会从点击的设备中动态获取
  const SSE_CONFIG = {
    // SSE 服务器端口（IP 地址会从设备中获取）
    port: 5000, // 请根据实际 SSE 服务器端口修改
    // SSE 端点路径
    endpoint: '/nodes', // 可根据实际后端 SSE 端点调整
    // 协议（http 或 https）
    protocol: 'http', // 根据实际协议修改
  };
  const deviceNoteList = ref<any[]>([]);
  const deviceAbnormalNoteList = ref<any[]>([]);
  const lastRefreshTime = ref('');
  const loading = ref(false);
  const devices = ref<any[]>([]);
  const nodes = ref<any[]>([]);
  const tabsactiveKey = ref('1');
  const treeData = ref<any[]>([]);
  const loadingTreeData = ref(false);
  const selectedKeys = ref<string[]>([]);
  const expandedKeys = ref<string[]>([]);
  const getDeviceInfoNUm = ref({
    busy: 0,
    free: 0,
    offline: 0,
  });
  // 接口运行异常数据
  const systemRunningConditionList = ref<any[]>([]);
  const systemRunningConditionColumns = [
    {
      title: '接口地址',
      dataIndex: 'url',
      key: 'url',
      width: 300,
    },
    {
      title: '失败类型',
      dataIndex: 'type',
      key: 'type',
      width: 100,
    },
    {
      title: '参数',
      dataIndex: 'args',
      key: 'args',
      width: 150,
    },
    {
      title: '响应结果',
      dataIndex: 'result',
      key: 'result',
      width: 300,
    },
    {
      title: '失败时间',
      dataIndex: 'time',
      key: 'time',
      width: 180,
    },
    {
      title: '失败次数',
      dataIndex: 'failCount',
      key: 'failCount',
      width: 100,
    },
  ];
  const clearAllAbnormalNodeListClick = () => {
    clearAllAbnormalNodeList()
      .then((res) => {
        if (res.code == 200) {
        }
      })
      .catch(() => {});
  };
  const handleFileDownload = (record: any) => {
    // 优先使用 download_url，如果没有则使用 url
    const downloadUrl = record.download_url || record.url;
    if (downloadUrl) {
      // 创建下载链接
      const a = document.createElement('a');
      a.href = downloadUrl;
      a.download = record.filename || 'download';
      // 模拟点击
      document.body.appendChild(a);
      a.click();
      // 移除元素，避免DOM冗余
      document.body.removeChild(a);
    }
  };

  // 格式化文件大小
  const formatFileSize = (bytes: number) => {
    if (!bytes || bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
  };

  // 格式化日期时间
  const formatDateTime = (dateTime: string) => {
    if (!dateTime) return '-';
    try {
      const date = new Date(dateTime);
      return date.toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
      });
    } catch (e) {
      return dateTime;
    }
  };
  const handleDeviceLogFilePathChange = (pagination) => {
    deviceLogFilePathPagination.value = { ...deviceLogFilePathPagination.value, ...pagination };
    loadDeviceLogFilePathList();
  };
  const deviceLogFilePathColumns = ref([
    { title: '文件名', dataIndex: 'filename', key: 'filename' },
    {
      title: '文件URL',
      dataIndex: 'url',
      key: 'url',
    },
  ]);
  const ModalShow = ref(false);
  const detailsRecord = ref({});
  const deviceLogFilePathLoading = ref(false);
  const deviceLogFilePathList = ref<any[]>([]);
  const deviceLogFilePathPagination = ref({
    total: 0,
    current: 1,
    pageSize: 10,
  });
  const detailsClick = (record) => {
    ModalShow.value = true;
    detailsRecord.value = record;
    // 如果是设备日志，加载文件路径列表
    loadDeviceLogFilePathList();
  };
  const loadDeviceLogFilePathList = () => {
    deviceLogFilePathLoading.value = true;

    const record = detailsRecord.value as any;
    if (!record) {
      deviceLogFilePathLoading.value = false;
      return;
    }

    // 获取设备 IP（从记录中获取，或从设备列表中查找）
    let deviceIp = record.deviceIp;
    if (!deviceIp && record.macAddress) {
      const selectedDevice = devices.value.find((device) => device.deviceMac === record.macAddress);
      if (selectedDevice && selectedDevice.deviceIp) {
        deviceIp = selectedDevice.deviceIp;
      }
    }

    // 如果仍然没有 IP，使用默认值或返回错误
    if (!deviceIp) {
      console.warn('无法获取设备 IP 地址');
      message.warning('无法获取设备 IP 地址');
      deviceLogFilePathLoading.value = false;
      return;
    }

    // 构建新的日志文件请求 URL
    // 格式：http://192.168.1.100:5000/api/get_log_files?node=detail_0&ip=192.168.1.100
    const nodeName = record.nodeName || '';
    const logUrl = `http://${deviceIp}:5000/api/get_log_files?node=${encodeURIComponent(nodeName)}&ip=${encodeURIComponent(deviceIp)}`;

    console.log('请求日志文件列表:', logUrl);

    // 使用 fetch 请求新的接口
    fetch(logUrl)
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
      })
      .then((data: any) => {
        // 解析新的数据结构：{ count, files, ip, message, node }
        if (data && data.files && Array.isArray(data.files)) {
          // 使用 files 数组作为数据源
          deviceLogFilePathList.value = [...data.files];
          // 使用 count 作为总数
          deviceLogFilePathPagination.value.total = data.count || data.files.length;
          console.log('日志文件列表加载成功:', {
            count: data.count,
            filesCount: data.files.length,
            ip: data.ip,
            node: data.node,
          });
        } else if (Array.isArray(data)) {
          // 如果直接返回数组（兼容旧格式）
          deviceLogFilePathList.value = [...data];
          deviceLogFilePathPagination.value.total = data.length;
        } else {
          // 默认处理
          deviceLogFilePathList.value = [];
          deviceLogFilePathPagination.value.total = 0;
          console.warn('未识别的数据格式:', data);
        }
        deviceLogFilePathLoading.value = false;
      })
      .catch((error) => {
        console.error('获取日志文件列表失败:', error);
        message.error('获取日志文件列表失败: ' + error.message);
        deviceLogFilePathLoading.value = false;
      });
  };
  const nodesList = ref([]);
  const activeKey = ref('');
  const handleChange = (key: string) => {
    console.log('折叠面板变化，key:', key);
    activeKey.value = key;

    // 如果选中了设备，使用该设备的 IP 建立 SSE 连接
    if (key) {
      // 从设备列表中查找对应的设备
      const selectedDevice = devices.value.find((device) => device.deviceMac === key);
      if (selectedDevice && selectedDevice.deviceIp) {
        console.log('找到设备，IP 地址:', selectedDevice.deviceIp);
        // 使用设备的 IP 建立 SSE 连接
        connectSSE(selectedDevice.deviceIp);
      } else {
        console.warn('未找到设备或设备没有 IP 地址');
        // 如果没有选中设备或设备没有 IP，断开 SSE 连接
        disconnectSSE();
      }
    } else {
      // 如果没有选中设备，断开 SSE 连接
      console.log('未选中设备，断开 SSE 连接');
      disconnectSSE();
    }
  };
  const dropdownnodeVisibles = ref([]);
  const startconfirm = (record, it) => {
    senddeviceNodecmd({ deviceMac: record.macAddress, deviceNode: record.nodeName, cmd: it }).then(
      (res) => {
        if (res.code == 200) {
          // message.success(t('sys.api.operationSuccess'));
        }
      },
    );
  };
  const devicenum = computed(() => {
    return devices.value.filter((node) => node.deviceStatus !== '0').length;
  });
  const filtertreeData = computed(() => {
    return treeData.value.filter((node) => node.key !== '0');
  });
  const normalColumns = [
    { title: t('sys.analysis.columns.mac'), dataIndex: 'macAddress', key: 'macAddress' },
    { title: t('sys.analysis.columns.node'), dataIndex: 'nodeName', key: 'nodeName' },
    { title: '状态', dataIndex: 'status', key: 'status' },
    { title: '正在执行', dataIndex: 'mark', key: 'mark' },
    { title: '超时时间（s）', dataIndex: 'outTime', key: 'outTime' },
    {
      title: t('sys.informaionMatchaing.columns.createTime'),
      dataIndex: 'lastUpdate',
      key: 'lastUpdate',
    },
    { title: t('sys.commentModal.action'), dataIndex: 'action', key: 'action' },
  ];
  const selectedRowKeys = ref([]);
  const recoveryNodeClick = (node) => {
    handleTimeoutMacNodeStatus([
      {
        deviceMac: node.macAddress,
        nodeName: node.nodeName,
      },
    ]).then((res) => {
      if (res.code == 200) {
        message.success(t('sys.api.operationSuccess'));
      }
    });
  };
  const normalColumns1 = [
    { title: t('sys.analysis.columns.mac'), dataIndex: 'macAddress', key: 'macAddress' },
    { title: t('sys.analysis.columns.node'), dataIndex: 'nodeName', key: 'nodeName' },
    {
      title: t('sys.informaionMatchaing.columns.deviceTimeOutNumber'),
      dataIndex: 'deviceTimeOutNumber',
      key: 'deviceTimeOutNumber',
    },
    {
      title: t('sys.informaionMatchaing.columns.lastTimeOutTime'),
      dataIndex: 'lastTimeOutTime',
      key: 'lastTimeOutTime',
    },
    { title: t('sys.commentModal.action'), dataIndex: 'action', key: 'action' },
  ];
  const handleExpand = (keys: string[], { expanded, node }) => {
    // 手风琴模式：每次只展开一个节点
    const isChildNode = node.key && node.key.includes('-');
    if (isChildNode) {
      return;
    }
    if (expanded) {
      expandedKeys.value = [node.key];
      if (abnormalRefreshTimer !== null) {
        clearInterval(abnormalRefreshTimer);
        abnormalRefreshTimer = null;
      }
    } else {
      expandedKeys.value = expandedKeys.value.filter((key) => key !== node.key);

      // 如果关闭的节点是当前选中的节点，则清空选中状态，显示全部数据
      if (selectedKeys.value.includes(node.key)) {
        selectedKeys.value = [];
        // 同时清除异常数据刷新定时器
        if (abnormalRefreshTimer !== null) {
          clearInterval(abnormalRefreshTimer);
          abnormalRefreshTimer = null;
        }
      }
    }
  };
  const splitMacAddress = (macString: string): string[] => {
    // 检查是否包含'-'分隔符
    if (macString.includes('-')) {
      // 拆分基本MAC部分和后缀
      const [baseMac, suffix] = macString.split('-');
      // 返回完整数组，包括后缀部分
      return [baseMac, suffix];
    }
    // 如果没有'-'，则直接按':'拆分
    return [macString];
  };
  const filteredDeviceNoteList = computed(() => {
    if (activeKey.value) {
      return deviceNoteList.value.filter((node) => node.macAddress == activeKey.value);
    } else {
      return deviceNoteList.value;
    }
    // if (selectedKeys.value.length === 0) return deviceNoteList.value;
    // // 将MAC地址格式的字符串拆分成数组

    // let newmacAddress = splitMacAddress(selectedKeys.value[0]);

    // if (newmacAddress.length > 1) {
    //   return deviceNoteList.value.filter(
    //     (node) => node.macAddress == newmacAddress[0] && node.nodeName == newmacAddress[1],
    //   );
    // } else {
    //   return deviceNoteList.value.filter((node) => node.macAddress == selectedKeys.value[0]);
    // }
  });
  const timeLength = ref(0);
  const filteredAbnormalNoteList = computed(() => {
    if (!activeKey.value) {
      tabsactiveKey.value = '1';
    }
    if (tabsactiveKey.value == '1' && activeKey.value) {
      timeLength.value = deviceAbnormalNoteList.value.filter(
        (node) => node.macAddress == activeKey.value && node.deviceTimeOutNumber <= 5,
      ).length;
      return deviceAbnormalNoteList.value.filter((node) => node.macAddress == activeKey.value);
    } else if (tabsactiveKey.value == '2' && activeKey.value) {
      timeLength.value = deviceAbnormalNoteList.value.filter(
        (node) => node.macAddress == activeKey.value && node.deviceTimeOutNumber <= 5,
      ).length;
      return deviceAbnormalNoteList.value.filter(
        (node) => node.macAddress == activeKey.value && node.deviceTimeOutNumber > 5,
      );
    } else if (tabsactiveKey.value == '3' && activeKey.value) {
      timeLength.value = deviceAbnormalNoteList.value.filter(
        (node) => node.macAddress == activeKey.value && node.deviceTimeOutNumber <= 5,
      ).length;
      return deviceAbnormalNoteList.value.filter(
        (node) => node.macAddress == activeKey.value && node.deviceTimeOutNumber <= 5,
      );
    } else {
      timeLength.value = deviceAbnormalNoteList.value.filter(
        (node) => node.deviceTimeOutNumber <= 5,
      ).length;
      return deviceAbnormalNoteList.value.filter((node) => node.deviceTimeOutNumber > 5);
    }

    // 将MAC地址格式的字符串拆分成数组
    // let newmacAddress = splitMacAddress(selectedKeys.value[0]);

    // if (newmacAddress.length > 1) {
    //   return deviceAbnormalNoteList.value.filter(
    //     (node) => node.macAddress == newmacAddress[0] && node.nodeName == newmacAddress[1],
    //   );
    // } else {
    //   return deviceAbnormalNoteList.value.filter((node) => node.macAddress == newmacAddress[0]);
    // }
  });

  // 定时器引用
  let refreshTimer: number | null = null;
  let abnormalRefreshTimer: number | null = null;

  // SSE 连接方法（使用设备的 IP 地址）
  const connectSSE = async (deviceIp?: string) => {
    // 如果已有连接，先断开
    if (eventSource) {
      disconnectSSE();
    }

    // 如果没有传入 IP，尝试从当前选中的设备获取
    if (!deviceIp && activeKey.value) {
      const selectedDevice = devices.value.find((device) => device.deviceMac === activeKey.value);
      if (selectedDevice && selectedDevice.deviceIp) {
        deviceIp = selectedDevice.deviceIp;
      }
    }

    // 如果仍然没有 IP，无法建立连接
    if (!deviceIp) {
      console.warn('无法建立 SSE 连接：缺少设备 IP 地址');
      return;
    }

    try {
      // 构建 SSE 端点 URL（使用设备的 IP 和配置的端口）
      // 格式：http://{设备IP}:{端口}{端点路径}
      const sseUrl = `${SSE_CONFIG.protocol}://${deviceIp}:${SSE_CONFIG.port}${SSE_CONFIG.endpoint}`;

      console.log('========== 正在连接 SSE ==========');
      console.log('设备 IP:', deviceIp);
      console.log('SSE URL:', sseUrl);
      console.log('==================================');

      // 创建 EventSource 连接
      eventSource = new EventSource(sseUrl);

      eventSource.onopen = () => {
        isSSEConnected.value = true;
        console.log('SSE 连接已建立');
        // SSE 连接成功后，可以选择停止定时刷新或保留作为备用
        // 如果使用 SSE，可以注释掉定时刷新
        // clearAutoRefresh();
      };

      eventSource.onmessage = (event) => {
        try {
          // 打印原始接收到的数据
          console.log('========== SSE 接收到的原始数据 ==========');
          console.log('原始数据 (event.data):', event.data);
          console.log('事件类型 (event.type):', event.type);
          console.log('事件 ID (event.id):', (event as any).id || '无');
          console.log('时间戳:', new Date().toLocaleString());
          console.log('==========================================');

          const data = JSON.parse(event.data);

          // 打印解析后的数据对象
          console.log('========== SSE 解析后的数据对象 ==========');
          console.log('完整数据对象:', data);
          console.log('数据类型 (data.type):', data.type);
          console.log('数据内容:', JSON.stringify(data, null, 2));
          console.log('==========================================');

          // 处理连接消息
          if (data.type === 'connected') {
            console.log('✅ SSE 数据流已连接:', data.message);
            isSSEConnected.value = true;
          }
          // 处理节点数据更新
          else if (data.type === 'nodeListUpdate') {
            console.log('📊 收到节点列表更新 (nodeListUpdate)');
            console.log('更新数据:', data.data);
            updateNodeListFromSSE(data.data);
          }
          // 处理 nodes 类型的更新（SSE 推送的节点列表）
          else if (data.type === 'nodes') {
            console.log('📊 收到节点列表更新 (nodes)');
            console.log('节点数据:', data.data);
            if (data.data && Array.isArray(data.data)) {
              // 将数据格式化为 updateNodeListFromSSE 期望的格式
              updateNodeListFromSSE({ deviceNoteList: data.data });
            } else {
              console.warn('⚠️ nodes 类型数据格式不正确:', data);
            }
          }
          // 处理设备信息更新
          else if (data.type === 'deviceInfoUpdate') {
            console.log('📈 收到设备信息更新 (deviceInfoUpdate)');
            console.log('设备信息数据:', data.data);
            console.log('⚠️ 当前节点列表数量（应该保持不变）:', deviceNoteList.value.length);
            if (data.data) {
              const deviceInfo = {
                busy: data.data.busy || 0,
                free: data.data.free || 0,
                offline: data.data.offline || 0,
              };
              console.log('更新后的设备信息:', deviceInfo);
              getDeviceInfoNUm.value = deviceInfo;
              console.log('✅ 设备信息已更新，节点列表数量保持不变:', deviceNoteList.value.length);
            }
          }
          // 处理心跳消息（保持连接）
          else if (data.type === 'heartbeat') {
            // 心跳消息，打印但使用较少的日志
            console.log('💓 SSE 心跳:', data.timestamp || new Date().toISOString());
          }
          // 处理错误消息
          else if (data.type === 'error') {
            console.error('❌ SSE 数据错误:', data.message);
            console.error('错误详情:', data);
          }
          // 处理通用数据更新（如果后端返回完整数据）
          else if (data.type === 'update') {
            console.log('🔄 收到通用数据更新 (update)');
            console.log('更新数据:', data);
            updateNodeListFromSSE(data);
          }
          // 如果没有 type 字段，可能是直接推送的数据
          else if (!data.type) {
            console.log('📦 收到无类型标识的数据，尝试作为节点列表更新');
            // 尝试直接作为节点列表数据更新
            updateNodeListFromSSE(data);
          }
          // 未知消息类型
          else {
            console.warn('⚠️ 未知的 SSE 消息类型:', data.type);
            console.warn('完整数据:', data);
            // 即使类型未知，也尝试更新节点列表（如果数据格式正确）
            if (data.deviceNoteList || data.deviceAbnormalNoteList || data.data) {
              console.log('尝试从未知类型数据中提取节点列表');
              updateNodeListFromSSE(data.data || data);
            }
          }
        } catch (error) {
          console.error('❌ 解析 SSE 数据失败:', error);
          console.error('原始数据 (event.data):', event.data);
          console.error('错误详情:', error);
          // 如果 JSON 解析失败，尝试直接打印原始数据
          console.log('尝试打印原始数据（可能是非 JSON 格式）:', event.data);
        }
      };

      eventSource.onerror = (error) => {
        console.error('SSE 连接错误:', error);
        console.error('EventSource 状态:', eventSource?.readyState);

        // readyState: 0=CONNECTING, 1=OPEN, 2=CLOSED
        if (eventSource?.readyState === EventSource.CLOSED) {
          isSSEConnected.value = false;
          console.warn('SSE 连接已关闭，将回退到定时刷新模式');
          // SSE 连接失败时，启动定时刷新作为备用
          if (!refreshTimer) {
            setupAutoRefresh();
          }
        } else if (eventSource?.readyState === EventSource.CONNECTING) {
          console.log('SSE 正在连接中...');
        }
      };
    } catch (error) {
      console.error('建立 SSE 连接失败:', error);
      isSSEConnected.value = false;
      // 连接失败时，启动定时刷新作为备用
      if (!refreshTimer) {
        setupAutoRefresh();
      }
    }
  };

  // 断开 SSE 连接
  const disconnectSSE = () => {
    if (eventSource) {
      eventSource.close();
      eventSource = null;
      isSSEConnected.value = false;
      console.log('SSE 连接已关闭');
    }
  };

  // 清理重复节点的函数
  const removeDuplicateNodes = () => {
    const uniqueNodes = new Map<string, any>();
    const seenKeys = new Set<string>();

    deviceNoteList.value.forEach((node: any) => {
      // 使用相同的唯一标识逻辑
      const key =
        node.macAddress && node.nodeName
          ? `${node.macAddress}_${node.nodeName}`
          : node.taskId || `${node.macAddress}_${node.nodeName}`;

      if (key && !seenKeys.has(key)) {
        seenKeys.add(key);
        uniqueNodes.set(key, node);
      } else if (key) {
        console.warn(`⚠️ 发现并移除重复节点: ${key}`);
      }
    });

    // 如果发现重复，更新列表
    if (uniqueNodes.size !== deviceNoteList.value.length) {
      console.log(`🧹 清理重复节点: ${deviceNoteList.value.length} -> ${uniqueNodes.size}`);
      deviceNoteList.value = Array.from(uniqueNodes.values());
    }
  };

  // 从 SSE 数据更新节点列表（统一处理函数）- 增量更新，避免表格闪烁
  const updateNodeListFromSSE = (sseData: any) => {
    if (!sseData) {
      console.warn('SSE 数据为空，无法更新节点列表');
      return;
    }

    console.log('========== updateNodeListFromSSE 开始 ==========');
    console.log('接收到的数据:', sseData);
    console.log('当前 deviceNoteList 长度:', deviceNoteList.value.length);
    console.log('当前 filteredDeviceNoteList 长度:', filteredDeviceNoteList.value.length);

    // 更新正常节点列表 - 使用增量更新策略，保持节点对象引用稳定
    // 注意：只有当 sseData.deviceNoteList 存在且为数组时才更新
    // 如果只有 deviceInfo 更新，不应该影响现有的节点列表
    if (sseData.deviceNoteList && Array.isArray(sseData.deviceNoteList)) {
      console.log('📊 增量更新正常节点列表');
      console.log('新数据节点数量:', sseData.deviceNoteList.length);

      // 如果收到的是空数组，说明可能是初始化或清空操作，但为了安全，我们不直接清空
      // 而是保持现有列表，除非明确要求清空
      if (sseData.deviceNoteList.length === 0) {
        console.warn('⚠️ 收到空节点列表，但保持现有节点列表不变');
        console.log('当前节点数量保持不变:', deviceNoteList.value.length);
        // 不更新节点列表，保持现有数据
        // 这样即使后端推送了空的 deviceNoteList，也不会清空表格
      } else {
        // 创建节点映射，用于快速查找（优先使用 macAddress+nodeName 作为唯一标识，因为 taskId 可能为空）
        // 保持原有节点对象的引用
        const nodeMap = new Map<string, { node: any; index: number }>();
        deviceNoteList.value.forEach((node: any, index: number) => {
          // 优先使用 macAddress+nodeName 作为唯一标识，因为这是最稳定的组合
          const key =
            node.macAddress && node.nodeName
              ? `${node.macAddress}_${node.nodeName}`
              : node.taskId || `${node.macAddress}_${node.nodeName}`;
          if (key) {
            // 如果同一个 key 已经存在，说明有重复，保留第一个（防止重复）
            if (!nodeMap.has(key)) {
              nodeMap.set(key, { node, index });
            } else {
              console.warn(`⚠️ 发现重复节点，已存在: ${key}，跳过重复项`);
            }
          }
        });

        console.log('📊 当前节点映射数量:', nodeMap.size);
        console.log('📊 当前节点列表数量:', deviceNoteList.value.length);

        // 记录需要添加的新节点
        const newNodes: any[] = [];

        // 遍历新数据，更新或添加节点
        sseData.deviceNoteList.forEach((newNode: any) => {
          // 使用相同的唯一标识逻辑
          const key =
            newNode.macAddress && newNode.nodeName
              ? `${newNode.macAddress}_${newNode.nodeName}`
              : newNode.taskId || `${newNode.macAddress}_${newNode.nodeName}`;
          if (key) {
            const existing = nodeMap.get(key);
            if (existing) {
              // 节点已存在，只更新状态等字段，保持节点对象引用不变
              const existingNode = existing.node;
              // 只更新变化的字段，避免触发不必要的响应式更新
              if (existingNode.status !== newNode.status) {
                existingNode.status = newNode.status;
                // 如果状态是空闲（status !== -1），将 mark 设置为空
                if (existingNode.status !== -1) {
                  existingNode.mark = '';
                  existingNode.outTime = '';
                }
              }
              if (existingNode.running !== newNode.running) {
                existingNode.running = newNode.running;
              }
              if (existingNode.lastUpdate !== newNode.lastUpdate) {
                existingNode.lastUpdate = newNode.lastUpdate;
              }
              // 更新其他可能变化的字段
              Object.keys(newNode).forEach((k) => {
                if (existingNode[k] !== newNode[k]) {
                  existingNode[k] = newNode[k];
                }
              });
              // 如果状态是空闲（status !== -1），确保 mark 为空
              if (existingNode.status !== -1 && existingNode.mark) {
                existingNode.mark = '';
                existingNode.outTime = '';
              }
            } else {
              // 节点不存在，记录需要添加
              // 如果状态是空闲（status !== -1），将 mark 设置为空
              const nodeToAdd = { ...newNode };
              if (nodeToAdd.status !== -1) {
                nodeToAdd.mark = '';
                nodeToAdd.outTime = '';
              }
              newNodes.push(nodeToAdd);
            }
          }
        });

        // 只添加新节点，不替换整个数组
        // 在添加前再次去重，防止重复添加
        const uniqueNewNodes = newNodes.filter((newNode) => {
          const key =
            newNode.macAddress && newNode.nodeName
              ? `${newNode.macAddress}_${newNode.nodeName}`
              : newNode.taskId || `${newNode.macAddress}_${newNode.nodeName}`;
          // 检查是否已存在于列表中
          const alreadyExists = deviceNoteList.value.some((existingNode) => {
            const existingKey =
              existingNode.macAddress && existingNode.nodeName
                ? `${existingNode.macAddress}_${existingNode.nodeName}`
                : existingNode.taskId || `${existingNode.macAddress}_${existingNode.nodeName}`;
            return existingKey === key;
          });
          if (alreadyExists) {
            console.warn(`⚠️ 跳过重复节点: ${key}`);
            return false;
          }
          return true;
        });

        if (uniqueNewNodes.length > 0) {
          deviceNoteList.value.push(...uniqueNewNodes);
          console.log(
            `✅ 添加了 ${uniqueNewNodes.length} 个新节点（过滤前: ${newNodes.length} 个）`,
          );
        }

        console.log('✅ 正常节点列表已增量更新，表格保持稳定');
        console.log('更新后节点数量:', deviceNoteList.value.length);
      }
    } else if (Array.isArray(sseData) && sseData.length > 0) {
      // 如果直接是数组，可能是正常节点列表
      console.log('📊 检测到数组格式数据，作为正常节点列表更新');
      // 使用增量更新，保持节点对象引用稳定
      const nodeMap = new Map<string, { node: any; index: number }>();
      deviceNoteList.value.forEach((node: any, index: number) => {
        // 优先使用 macAddress+nodeName 作为唯一标识
        const key =
          node.macAddress && node.nodeName
            ? `${node.macAddress}_${node.nodeName}`
            : node.taskId || `${node.macAddress}_${node.nodeName}`;
        if (key) {
          // 如果同一个 key 已经存在，说明有重复，保留第一个
          if (!nodeMap.has(key)) {
            nodeMap.set(key, { node, index });
          } else {
            console.warn(`⚠️ 发现重复节点，已存在: ${key}，跳过重复项`);
          }
        }
      });

      const newNodes: any[] = [];

      sseData.forEach((newNode: any) => {
        // 使用相同的唯一标识逻辑
        const key =
          newNode.macAddress && newNode.nodeName
            ? `${newNode.macAddress}_${newNode.nodeName}`
            : newNode.taskId || `${newNode.macAddress}_${newNode.nodeName}`;
        if (key) {
          const existing = nodeMap.get(key);
          if (existing) {
            // 只更新变化的字段
            const existingNode = existing.node;
            if (existingNode.status !== newNode.status) {
              existingNode.status = newNode.status;
              // 如果状态是空闲（status !== -1），将 mark 设置为空
              if (existingNode.status !== -1) {
                existingNode.mark = '';
              }
            }
            if (existingNode.running !== newNode.running) {
              existingNode.running = newNode.running;
            }
            if (existingNode.lastUpdate !== newNode.lastUpdate) {
              existingNode.lastUpdate = newNode.lastUpdate;
            }
            Object.keys(newNode).forEach((k) => {
              if (existingNode[k] !== newNode[k]) {
                existingNode[k] = newNode[k];
              }
            });
            // 如果状态是空闲（status !== -1），确保 mark 为空
            if (existingNode.status !== -1 && existingNode.mark) {
              existingNode.mark = '';
              existingNode.outTime = '';
            }
          } else {
            // 节点不存在，记录需要添加
            // 如果状态是空闲（status !== -1），将 mark 设置为空
            const nodeToAdd = { ...newNode };
            if (nodeToAdd.status !== -1) {
              nodeToAdd.mark = '';
              nodeToAdd.outTime = '';
            }
            newNodes.push(nodeToAdd);
          }
        }
      });

      // 只添加新节点，在添加前再次去重
      const uniqueNewNodes = newNodes.filter((newNode) => {
        const key =
          newNode.macAddress && newNode.nodeName
            ? `${newNode.macAddress}_${newNode.nodeName}`
            : newNode.taskId || `${newNode.macAddress}_${newNode.nodeName}`;
        // 检查是否已存在于列表中
        const alreadyExists = deviceNoteList.value.some((existingNode) => {
          const existingKey =
            existingNode.macAddress && existingNode.nodeName
              ? `${existingNode.macAddress}_${existingNode.nodeName}`
              : existingNode.taskId || `${existingNode.macAddress}_${existingNode.nodeName}`;
          return existingKey === key;
        });
        if (alreadyExists) {
          console.warn(`⚠️ 跳过重复节点: ${key}`);
          return false;
        }
        return true;
      });

      if (uniqueNewNodes.length > 0) {
        deviceNoteList.value.push(...uniqueNewNodes);
        console.log(`✅ 添加了 ${uniqueNewNodes.length} 个新节点（过滤前: ${newNodes.length} 个）`);
      }
      console.log('✅ 节点列表已增量更新到表格');
    }

    // 更新异常节点列表 - 使用原逻辑（直接替换）
    if (sseData.deviceAbnormalNoteList && Array.isArray(sseData.deviceAbnormalNoteList)) {
      console.log('⚠️ 更新异常节点列表');
      console.log('异常节点数据:', sseData.deviceAbnormalNoteList);
      console.log('异常节点数量:', sseData.deviceAbnormalNoteList.length);
      deviceAbnormalNoteList.value = sseData.deviceAbnormalNoteList;
      console.log('✅ 异常节点列表已更新到表格');
    }

    // 更新设备信息
    // 注意：设备信息更新不应该影响节点列表
    // 即使只有 deviceInfo 更新，也应该保持现有的节点列表不变
    if (sseData.deviceInfo) {
      console.log('📈 更新设备信息');
      console.log('设备信息数据:', sseData.deviceInfo);
      console.log('当前节点列表数量:', deviceNoteList.value.length);
      getDeviceInfoNUm.value = {
        busy: sseData.deviceInfo.busy || 0,
        free: sseData.deviceInfo.free || 0,
        offline: sseData.deviceInfo.offline || 0,
      };
      console.log('✅ 设备信息已更新，节点列表保持不变');
      console.log('更新后的设备信息:', getDeviceInfoNUm.value);
    }

    // 更新刷新时间
    const now = new Date();
    lastRefreshTime.value = now.toLocaleTimeString(undefined, {
      hour12: false,
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
    });

    // 更新完成后，清理可能的重复节点
    removeDuplicateNodes();

    console.log('========== updateNodeListFromSSE 结束 ==========');
    console.log('✅ 节点列表通过 SSE 增量更新成功，表格保持稳定');
    console.log('当前正常节点数量 (deviceNoteList):', deviceNoteList.value.length);
    console.log(
      '当前过滤后节点数量 (filteredDeviceNoteList):',
      filteredDeviceNoteList.value.length,
    );
    console.log('当前异常节点数量:', deviceAbnormalNoteList.value.length);
    console.log('当前 activeKey:', activeKey.value);
    console.log('===============================================');
  };

  // 构建树形数据
  const buildTreeData = () => {
    const tree: any[] = [];

    // 为每个设备创建一个节点
    devices.value.forEach((device) => {
      const deviceNode = {
        key: device.deviceMac,
        ip: device.deviceIp,
        status: '设备',
        deviceStatus: device.deviceStatus || '正常',
        title: device.deviceMac || '未命名设备',
        children: [] as any[],
      };

      // 将属于该设备的节点添加为子节点

      tree.push(deviceNode);
    });

    return tree;
  };
  const nodeCmds = ref([]);
  // 获取设备和节点数据
  const fetchDeviceAndNodeData = async () => {
    loadingTreeData.value = true;
    try {
      // 并行获取设备和节点数据
      const [deviceResponse, postCmdPageResponse] = await Promise.all([
        getDeviceListAPI(),
        postCmdPage({
          current: 1,
          size: 20,
        }),
      ]);
      nodeCmds.value = postCmdPageResponse.data.records.filter((item) => item.cmdType == 2) || [];
      console.log(nodeCmds.value);

      if (deviceResponse.code === 200) {
        devices.value = deviceResponse.data || [];
        treeData.value = buildTreeData();
      } else {
        console.error('获取设备或节点数据失败');
      }
    } catch (error) {
      console.error('获取设备和节点数据出错:', error);
    } finally {
      loadingTreeData.value = false;
    }
  };

  // 获取节点列表数据
  const fetchNodeList = async () => {
    if (loading.value) return; // 防止重复请求

    // 如果 SSE 已连接，不通过 API 刷新节点列表，避免覆盖 SSE 实时数据
    if (isSSEConnected.value) {
      console.log('⚠️ SSE 已连接，跳过 API 刷新节点列表，避免覆盖实时数据');
      // 只更新设备信息，不更新节点列表
      try {
        const resgetDeviceInfo = await getDeviceInfo();
        if (resgetDeviceInfo.code == 200) {
          getDeviceInfoNUm.value = resgetDeviceInfo.data;
        }
      } catch (error) {
        console.error('获取设备信息失败:', error);
      }
      return;
    }

    loading.value = true;
    try {
      const response = await queryNoteList();
      const resgetDeviceInfo = await getDeviceInfo();
      if (resgetDeviceInfo.code == 200) {
        getDeviceInfoNUm.value = resgetDeviceInfo.data;
      }
      // 确保loading状态总是会被重置
      loading.value = false;

      if (response.code === 200) {
        // 只有当返回的数据不为空时才更新，避免清空现有数据
        const newDeviceNoteList = response.data.deviceNoteList || [];
        const newDeviceAbnormalNoteList = response.data.deviceAbnormalNoteList || [];

        // 如果返回的数据不为空，才更新列表
        // 如果返回空数组，说明可能没有数据，但为了安全，我们只在有数据时才更新
        if (newDeviceNoteList.length > 0 || deviceNoteList.value.length === 0) {
          // 处理 mark 和 outTime 字段：如果状态是空闲（status !== -1），将 mark 和 outTime 设置为空
          newDeviceNoteList.forEach((node: any) => {
            if (node.status !== -1) {
              node.mark = '';
              node.outTime = '';
            }
          });
          deviceNoteList.value = newDeviceNoteList;
        } else {
          console.log(
            '⚠️ API 返回空节点列表，保持现有数据不变。当前节点数量:',
            deviceNoteList.value.length,
          );
        }

        // 异常节点列表可以直接更新
        deviceAbnormalNoteList.value = newDeviceAbnormalNoteList;

        // 格式化刷新时间
        const now = new Date();
        lastRefreshTime.value = now.toLocaleTimeString(undefined, {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit',
        });

        console.log('节点列表更新成功，当前数量:', deviceNoteList.value.length);
      } else {
        console.error(t('sys.queryNoteList.fetchFailed') + ':', response.message);
      }
    } catch (error) {
      console.error(t('sys.queryNoteList.fetchError') + ':', error);
      loading.value = false;
    }
  };

  // 设置定时刷新
  const setupAutoRefresh = () => {
    // 先清除之前可能存在的定时器，避免多个定时器同时运行
    clearAutoRefresh();

    fetchNodeList();

    // 设置每秒刷新一次的定时器
    refreshTimer = window.setInterval(() => {
      fetchNodeList();
    }, 3000);
  };

  // 清理定时器
  const clearAutoRefresh = () => {
    if (refreshTimer !== null) {
      clearInterval(refreshTimer);
      refreshTimer = null;
    }
    // 同时清理异常数据刷新定时器
    if (abnormalRefreshTimer !== null) {
      clearInterval(abnormalRefreshTimer);
      abnormalRefreshTimer = null;
    }
  };

  // 处理树形选择变化
  const onSelectChange = (selectedKeysValue: string[]) => {
    selectedKeys.value = selectedKeysValue;
    console.log('Selected keys:', selectedKeysValue);

    // 仅处理选中设备的情况（第一个选中项是设备）
    if (selectedKeysValue.length > 0) {
      const selectedDeviceId = selectedKeysValue[0];

      // 先获取节点数据并更新树形结构

      // 设置每3秒刷新一次异常节点数据
      // 先清除之前可能存在的定时器
      if (abnormalRefreshTimer !== null) {
        clearInterval(abnormalRefreshTimer);
        abnormalRefreshTimer = null;
      }

      // 设置定时器，每3秒调用一次
      abnormalRefreshTimer = window.setInterval(() => {
        getNodeListAPI(selectedDeviceId).then((nodeResponse) => {
          if (nodeResponse.code === 200) {
            nodes.value = nodeResponse.data || [];

            // 更新树数据，将子节点添加到对应的设备节点下
            const updatedTreeData = JSON.parse(JSON.stringify(treeData.value));

            // 查找选中的设备节点
            const findAndUpdateDeviceNode = (nodes: any[]) => {
              for (let i = 0; i < nodes.length; i++) {
                if (nodes[i].key == selectedDeviceId) {
                  // 清空原有的子节点并添加新节点
                  nodes[i].children = [];
                  const deviceNodes = nodeResponse.data.filter(
                    (node: any) => node.mac === selectedDeviceId,
                  );

                  deviceNodes.forEach((node: any) => {
                    nodes[i].children.push({
                      key: nodes[i].key + '-' + node.node,
                      status: '节点',
                      nodeStatus: node.status,
                      title: node.node || '未命名节点',
                    });
                  });
                  return true;
                }
                // 递归查找子节点
                if (nodes[i].children && nodes[i].children.length > 0) {
                  if (findAndUpdateDeviceNode(nodes[i].children)) {
                    return true;
                  }
                }
              }
              return false;
            };

            // 执行更新
            findAndUpdateDeviceNode(updatedTreeData);

            // 更新树数据，触发视图更新
            treeData.value = updatedTreeData;
          } else {
            console.error('获取节点数据失败');
          }
        });
      }, 3000);
    } else {
      // 如果没有选中任何节点，清除异常数据刷新定时器
      if (abnormalRefreshTimer !== null) {
        clearInterval(abnormalRefreshTimer);
        abnormalRefreshTimer = null;
      }
    }
  };

  // 接口运行异常定时器
  let systemRunningConditionTimer: NodeJS.Timeout | null = null;

  // 获取接口运行异常数据
  const fetchSystemRunningCondition = () => {
    getSystemRunningCondition()
      .then((res) => {
        if (res && res.code === 200 && res.data && res.data.urlConditionList) {
          systemRunningConditionList.value = res.data.urlConditionList || [];
        } else {
          systemRunningConditionList.value = [];
        }
      })
      .catch((err) => {
        console.error('获取接口运行异常数据失败:', err);
        systemRunningConditionList.value = [];
      });
  };

  // 清除接口运行异常定时器
  const clearSystemRunningConditionTimer = () => {
    if (systemRunningConditionTimer) {
      clearInterval(systemRunningConditionTimer);
      systemRunningConditionTimer = null;
    }
  };

  // 生命周期钩子
  onMounted(() => {
    // 确保组件挂载时没有运行中的定时器
    clearAutoRefresh();
    // 获取设备和节点数据构建树形结构
    fetchDeviceAndNodeData();
    // 获取接口运行异常数据
    fetchSystemRunningCondition();
    // 设置定时刷新接口运行异常数据（每2秒刷新一次）
    systemRunningConditionTimer = setInterval(() => {
      fetchSystemRunningCondition();
    }, 2000);
    // 尝试连接 SSE，如果失败则回退到定时刷新
    // connectSSE();
    // 如果 SSE 连接失败，setupAutoRefresh 会在 connectSSE 的错误处理中调用
    // 为了保险起见，也可以先启动定时刷新作为备用
    // setupAutoRefresh();
    // 添加页面可见性变化监听，防止页面不可见时仍在刷新
    document.addEventListener('visibilitychange', handleVisibilityChange);
  });

  onUnmounted(() => {
    // 断开 SSE 连接
    disconnectSSE();
    // 断开日志 SSE 连接
    disconnectLogSSE();
    // 清理定时器
    clearAutoRefresh();
    // 清除接口运行异常定时器
    clearSystemRunningConditionTimer();
    // 移除页面可见性监听
    document.removeEventListener('visibilitychange', handleVisibilityChange);
  });

  // 处理页面可见性变化
  const handleVisibilityChange = () => {
    if (document.hidden) {
      // 页面不可见时暂停刷新和 SSE
      disconnectSSE();
      clearAutoRefresh();
    } else {
      // 页面可见时恢复 SSE 连接和刷新
      // 如果当前有选中的设备，尝试恢复 SSE 连接
      if (activeKey.value) {
        const selectedDevice = devices.value.find((device) => device.deviceMac === activeKey.value);
        if (selectedDevice && selectedDevice.deviceIp && !isSSEConnected.value) {
          connectSSE(selectedDevice.deviceIp);
        }
      }
      // 如果 SSE 未连接，则启动定时刷新
      if (!isSSEConnected.value && !refreshTimer) {
        setupAutoRefresh();
      }
    }
  };
  const TemPlechecked1 = ref('1');

  // ==================== 实时日志相关方法 ====================

  // 打开实时日志模态框
  const openRealtimeLog = (record: any) => {
    console.log('打开实时日志，记录:', record);
    realtimeLogRecord.value = record;
    realtimeLogModalVisible.value = true;
    // 清空之前的日志
    logLines.value = [];
    logStats.value = { totalLines: 0 };
    logComplete.value = false;
    // 连接日志 SSE
    connectLogSSE(record);
  };

  // 关闭实时日志模态框
  const closeRealtimeLog = () => {
    console.log('关闭实时日志');
    disconnectLogSSE();
    realtimeLogModalVisible.value = false;
    realtimeLogRecord.value = null;
    logLines.value = [];
    logStats.value = null;
    logComplete.value = false;
  };

  // 清空日志行
  const clearLogLines = () => {
    logLines.value = [];
    if (logStats.value) {
      logStats.value.totalLines = 0;
    }
  };

  // 切换日志连接
  const toggleLogConnection = () => {
    if (logIsConnected.value) {
      disconnectLogSSE();
    } else {
      if (realtimeLogRecord.value) {
        connectLogSSE(realtimeLogRecord.value);
      }
    }
  };

  // 连接日志 SSE
  const connectLogSSE = async (record: any) => {
    // 如果已有连接，先断开
    if (logEventSource) {
      disconnectLogSSE();
    }

    // 获取设备 IP（从记录中获取，或从当前选中的设备获取）
    let deviceIp = record.deviceIp;
    if (!deviceIp && activeKey.value) {
      const selectedDevice = devices.value.find((device) => device.deviceMac === activeKey.value);
      if (selectedDevice && selectedDevice.deviceIp) {
        deviceIp = selectedDevice.deviceIp;
      }
    }

    // 如果仍然没有 IP，无法建立连接
    if (!deviceIp) {
      console.warn('无法建立日志 SSE 连接：缺少设备 IP 地址');
      message.warning('无法建立日志连接：缺少设备 IP 地址');
      return;
    }

    try {
      // 构建日志 SSE 端点 URL
      // 格式：http://{设备IP}:{端口}/logs?mac={macAddress}&node={nodeName}
      const logSseUrl = `${SSE_CONFIG.protocol}://${deviceIp}:${SSE_CONFIG.port}/logs?mac=${record.macAddress || ''}&node=${record.nodeName || ''}`;

      console.log('========== 正在连接日志 SSE ==========');
      console.log('设备 IP:', deviceIp);
      console.log('MAC 地址:', record.macAddress);
      console.log('节点名称:', record.nodeName);
      console.log('日志 SSE URL:', logSseUrl);
      console.log('==================================');

      // 创建 EventSource 连接
      logEventSource = new EventSource(logSseUrl);

      logEventSource.onopen = () => {
        logIsConnected.value = true;
        console.log('✅ 日志 SSE 连接已建立');
        if (!logStats.value) {
          logStats.value = { totalLines: 0 };
        }
      };

      logEventSource.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);

          // 处理连接消息
          if (data.type === 'connected') {
            console.log('✅ 日志流已连接:', data.message);
            logIsConnected.value = true;
            if (!logStats.value) {
              logStats.value = { totalLines: 0 };
            }
          }
          // 处理日志消息
          else if (data.type === 'log') {
            // 格式：{"type": "log", "message": "...", "timestamp": "..."}
            if (!logStats.value) {
              logStats.value = { totalLines: 0 };
            }
            logStats.value.totalLines++;
            logLines.value.push({
              line_number: logStats.value.totalLines,
              content: data.message,
              timestamp: data.timestamp,
            });

            // 保持显示最新的1000行
            if (logLines.value.length > 1000) {
              logLines.value.shift();
            }

            // 自动滚动到底部
            nextTick(() => {
              scrollLogToBottom();
            });
          }
          // 处理心跳消息（保持连接）
          else if (data.type === 'heartbeat') {
            // 心跳消息，不显示，仅用于保持连接
            // console.log('💓 日志心跳:', data.timestamp);
          }
          // 处理完成消息
          else if (data.type === 'complete') {
            logComplete.value = true;
            console.log('✅ 日志读取完成');
          }
          // 处理错误消息
          else if (data.type === 'error') {
            console.error('❌ 日志读取错误:', data.message);
            message.error('日志读取错误: ' + data.message);
          }
          // 未知消息类型
          else {
            console.warn('⚠️ 未知的日志消息类型:', data.type, data);
          }
        } catch (error) {
          console.error('❌ 解析日志 SSE 数据失败:', error);
          console.error('原始数据:', event.data);
        }
      };

      logEventSource.onerror = (error) => {
        console.error('❌ 日志 SSE 连接错误:', error);
        console.error('EventSource 状态:', logEventSource?.readyState);

        // readyState: 0=CONNECTING, 1=OPEN, 2=CLOSED
        if (logEventSource?.readyState === EventSource.CLOSED) {
          logIsConnected.value = false;
          console.warn('⚠️ 日志 SSE 连接已关闭');
        } else if (logEventSource?.readyState === EventSource.CONNECTING) {
          console.log('🔄 日志 SSE 正在连接中...');
        }
      };
    } catch (error) {
      console.error('❌ 建立日志 SSE 连接失败:', error);
      logIsConnected.value = false;
      message.error('建立日志连接失败');
    }
  };

  // 断开日志 SSE 连接
  const disconnectLogSSE = () => {
    if (logEventSource) {
      logEventSource.close();
      logEventSource = null;
      logIsConnected.value = false;
      console.log('✅ 日志 SSE 连接已关闭');
    }
  };

  // 滚动日志到底部
  const scrollLogToBottom = () => {
    if (logContainerRef.value) {
      logContainerRef.value.scrollTop = logContainerRef.value.scrollHeight;
    }
  };

  // 格式化日志时间戳
  const formatLogTimestamp = (timestamp: string | number) => {
    if (!timestamp) return '';
    try {
      const date = new Date(timestamp);
      return date.toLocaleTimeString('zh-CN', {
        hour12: false,
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        fractionalSecondDigits: 3,
      });
    } catch (e) {
      return String(timestamp);
    }
  };
</script>

<style lang="less" scoped>
  // 响应式调整
  @media (max-width: 768px) {
    .node-monitor-container {
      .overview-card {
        .overview-content {
          flex-direction: column;
          gap: 16px;

          .overview-divider {
            width: 100px;
            height: 1px;
            margin: 8px 0;
          }
        }
      }

      .card-grid {
        grid-template-columns: 1fr;
      }
    }
  }

  // 响应式调整
  @media (max-width: 1200px) {
    .node-monitor-container {
      .tree-card {
        margin-right: 0;
        margin-bottom: 16px;
      }
    }
  }

  .node-monitor-container {
    // 概览卡片样式
    .overview-card {
      border: none;
      border-radius: 12px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;

      .group-title {
        margin-bottom: 16px;
        font-size: 20px;
      }

      .overview-content {
        display: flex;
        align-items: center;
        justify-content: space-around;
        padding: 16px 0;

        .overview-item {
          flex: 1;
          text-align: center;

          &.full-width {
            flex: 2;
          }

          .overview-label {
            margin-bottom: 8px;
            opacity: 0.9;
            font-size: 20px;
          }

          .overview-value {
            transition: all 0.3s ease;
            font-size: 32px;
            font-weight: bold;

            &.normal-count {
              color: #52c41a;
            }

            &.abnormal-count {
              color: #ff4d4f;
            }
          }

          .overview-time {
            display: inline-block;
            padding: 4px 12px;
            border-radius: 20px;
            background: rgb(255 255 255 / 20%);
            font-size: 16px;
            font-weight: 500;
          }
        }

        .overview-divider {
          width: 1px;
          height: 40px;
          margin: 0 16px;
          background: rgb(255 255 255 / 30%);
        }
      }
    }

    // 内容区域样式
    .content-wrapper {
      padding-bottom: 24px;
    }

    // 树形卡片样式
    .tree-card {
      height: fit-content;
      margin-right: 16px;
    }

    .status-dot {
      display: inline-block;
      width: 8px;
      height: 8px;
      margin: 0 8px;
      border-radius: 50%;
    }

    .red-dot {
      background-color: #ff4d4f;
    }

    .green-dot {
      background-color: #52c41a;
    }

    // 节点区域样式
    .node-section {
      &.mt-6 {
        margin-top: 24px;
      }

      .section-header {
        margin-bottom: 16px;

        .section-title {
          display: flex;
          align-items: center;
          margin: 0;
          color: #1f2937;
          font-size: 20px;
          font-weight: 600;

          .title-icon {
            margin-right: 8px;
            font-size: 24px;

            &.normal-icon {
              color: #52c41a;
            }

            &.abnormal-icon {
              color: #ff4d4f;
            }
          }
        }
      }

      // 卡片网格布局
      .card-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
        gap: 16px;
      }

      // 节点卡片样式
      .node-card {
        position: relative;
        overflow: hidden;
        transition: all 0.3s ease;
        border-radius: 12px;

        &:hover {
          transform: translateY(-4px);
          box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
        }

        &::before {
          content: '';
          position: absolute;
          top: 0;
          left: 0;
          width: 4px;
          height: 100%;
        }

        &.normal-card {
          border: 1px solid #f0f0f0;

          &::before {
            background: #52c41a;
          }
        }

        &.abnormal-card {
          border: 1px solid #ffccc7;
          background-color: #fff2f0;

          &::before {
            background: #ff4d4f;
          }
        }

        .card-header {
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 16px 20px 12px;
          border-bottom: 1px solid #f0f0f0;

          .card-title {
            margin: 0;
            color: #1f2937;
            font-size: 16px;
            font-weight: 600;
          }

          .outtime-tag {
            font-weight: 500;
          }

          .timeout-badge {
            .ant-tag {
              background-color: #ff4d4f;
              color: white;
            }
          }
        }

        .card-body {
          padding: 16px 20px;

          .info-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;

            &:last-child {
              margin-bottom: 0;
            }

            &.full-width {
              flex-direction: column;
              align-items: flex-start;

              .info-value {
                width: 100%;
                margin-top: 4px;
              }
            }

            .info-label {
              color: #6b7280;
              font-size: 14px;
              font-weight: 500;
            }

            .info-value {
              color: #1f2937;
              font-size: 14px;
              font-weight: 500;

              &.timeout-count {
                color: #ff4d4f;
                font-weight: 600;
              }

              &.ellipsis {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
            }
          }
        }
      }

      // 空状态样式
      .empty-state {
        padding: 48px 24px;
        border: 1px dashed #d9d9d9;
        border-radius: 12px;
        background: #fafafa;
        text-align: center;
      }
    }
  }

  /* 增强卡片网格布局 */
  .enhanced-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 24px;
  }

  /* 增强卡片样式 */
  .enhanced-card {
    overflow: hidden;
    transition: all 0.3s ease;
    border: 1px solid #e8e8e8;
    border-radius: 12px;
    background-color: #fff;
    box-shadow: 0 2px 8px rgb(0 0 0 / 5%);

    &:hover {
      transform: translateY(-2px);
      border-color: #4096ff;
      box-shadow: 0 4px 16px rgb(0 0 0 / 8%);
    }
  }

  /* 正常节点卡片特定样式 */
  .normal-card {
    border-left: 4px solid #52c41a;

    &:hover {
      border-left-color: #73d13d;
    }
  }

  /* 标题图标样式 */
  .title-icon {
    margin-right: 8px;
    font-size: 18px;
    vertical-align: middle;
  }

  .normal-icon {
    color: #52c41a;
  }

  .overview-content-flexs {
    display: flex;
  }

  .overview-group {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  // ==================== 实时日志样式 ====================
  .realtime-log-container {
    .log-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 16px;
      padding: 12px;
      border-radius: 8px;
      background: #f5f5f5;

      .log-status-info {
        display: flex;
        align-items: center;
        gap: 12px;
      }

      .log-status-badge {
        padding: 4px 12px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 500;

        &.tailing {
          background: #d1ecf1;
          color: #0c5460;
        }

        &.disconnected {
          background: #f8d7da;
          color: #721c24;
        }
      }

      .log-stats-header {
        padding: 4px 12px;
        border-radius: 4px;
        background: #e7f3ff;
        color: #06c;
        font-size: 12px;
        font-weight: 500;
      }

      .log-complete-badge {
        padding: 4px 12px;
        border-radius: 4px;
        background: #d4edda;
        color: #155724;
        font-size: 12px;
        font-weight: 500;
      }

      .log-actions {
        display: flex;
        gap: 8px;
      }
    }

    .log-display-container {
      max-height: 600px;
      padding: 16px;
      overflow-y: auto;
      border: 2px solid #333;
      border-radius: 8px;
      background: #1e1e1e;
      color: #d4d4d4;
      font-family: 'Courier New', Consolas, monospace;
      font-size: 13px;
      line-height: 1.6;

      .log-line-item {
        display: flex;
        align-items: flex-start;
        padding: 4px 0;
        border-bottom: 1px solid #2d2d2d;
        color: #d4d4d4;
        gap: 12px;

        &:last-child {
          border-bottom: none;
        }

        &:hover {
          background: rgb(255 255 255 / 5%);
        }

        .log-line-number {
          flex-shrink: 0;
          min-width: 60px;
          color: #858585;
          font-weight: bold;
          text-align: right;
        }

        .log-line-content {
          flex: 1;
          word-break: break-word;
          white-space: pre-wrap;
        }

        .log-line-timestamp {
          flex-shrink: 0;
          min-width: 100px;
          color: #858585;
          font-size: 11px;
          text-align: right;
        }
      }

      .log-loading {
        padding: 40px 20px;
        color: #858585;
        font-style: italic;
        text-align: center;
      }
    }
  }

  // ==================== 状态文本样式 ====================
  .status-text {
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;

    &.status-free {
      background: #d4edda;
      color: #155724;
    }

    &.status-busy {
      background: #fff3cd;
      color: #856404;
    }

    &.status-offline {
      background: #f8d7da;
      color: #721c24;
    }
  }
</style>
